package com.hframe.http.client;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.hframe.Json.DataToJson;
import com.hframe.Json.JsonToData;
import com.hframe.data.DRef;
import com.hframe.type.TArray;
import com.hframe.type.TLong;
import com.hframe.type.TString;
import com.hframe.utils.FileUtil;
import com.sun.istack.internal.NotNull;
import org.apache.log4j.Logger;

import java.io.FileReader;
import java.io.IOException;
import java.net.*;
import java.util.*;

/**
 * Created by heqian on 17-6-29.
 */

public class HttpCookies extends CookieManager {
    private static Logger log = Logger.getLogger(CookieManager.class);

    public HttpCookies() {
        super(new HttpCookieStore(), new HttpCookiePolicy());
    }

    public HttpCookies(CookieStore cookieStore) {
        super(cookieStore, new HttpCookiePolicy());
    }

    public static class HttpCookiePolicy implements CookiePolicy {
        public boolean shouldAccept(URI uri, HttpCookie cookie) {
            return true;
        }
    }

    public static class HttpCookieFile extends HttpCookieStore {
        protected String filePath = null;
        private Object lock = new Object();

        public HttpCookieFile(@NotNull String filePath) {
            this.filePath = filePath;
        }

        @Override
        public void add(URI uri, HttpCookie cookie) {
            synchronized (lock) {
                super.add(uri, cookie);
                TArray<DataCookieMap> typeArray = new TArray<>(DataCookieMap.class);
                for (Map.Entry<String, List<HttpCookie>> item : map.entrySet()) {
                    DataCookieMap cookieMap = new DataCookieMap();
                    cookieMap.key.set(item.getKey());

                    for (HttpCookie httpCookie : item.getValue()) {
                        DataCookie dataCookie = new DataCookie();

                        dataCookie.domain.set(httpCookie.getDomain());
                        dataCookie.name.set(httpCookie.getName());
                        dataCookie.value.set(httpCookie.getValue());
                        dataCookie.maxAge.set(httpCookie.getMaxAge());
                        dataCookie.path.set(httpCookie.getPath());

                        cookieMap.list.add(dataCookie);
                    }
                    typeArray.add(cookieMap);
                }

                FileUtil.SaveString(DataToJson.convert(typeArray).toString(), filePath);
            }
        }

        @Override
        public List<HttpCookie> get(URI uri) {
            synchronized (lock) {
                TArray<DataCookieMap> typeArray = new TArray<>(DataCookieMap.class);
                map.clear();
                try {
                    JsonElement json = new JsonParser().parse(new FileReader(filePath));
                    JsonToData.convert(json, typeArray);
                    for (DataCookieMap cookieMap : typeArray.get()) {
                        List<HttpCookie> list = new ArrayList<>();

                        for (DataCookie dataCookie : cookieMap.list.get()) {
                            HttpCookie cookie = new HttpCookie(dataCookie.name.get(), dataCookie.value.get());
                            cookie.setPath(dataCookie.path.get());
                            cookie.setDomain(dataCookie.domain.get());
                            cookie.setMaxAge(dataCookie.maxAge.get());
                            list.add(cookie);
                        }

                        map.put(cookieMap.key.get(), list);
                    }
                } catch (Exception e) {
                    log.error(e.toString(), e);
                }
                return super.get(uri);
            }
        }
    }

    public static class DataCookieMap extends DRef<DataCookie> {
        public TString key = new TString();
        public TArray<DataCookie> list = new TArray<>(DataCookie.class);
    }

    public static class DataCookie extends DRef<DataCookie> {
        public TLong maxAge = new TLong();
        public TString value = new TString();
        public TString path = new TString();
        public TString name = new TString();
        public TString domain = new TString();
    }

    public static class HttpCookieStore implements CookieStore {
        protected Map<String, List<HttpCookie>> map = new HashMap<>();
        private Object lock = new Object();

        public void add(URI uri, HttpCookie cookie) {
            synchronized (lock) {

                String key = cookie.getDomain() + cookie.getPath();
                List<HttpCookie> cookies = map.get(key);
                if (cookies == null)
                    map.put(key, cookies = new ArrayList<>());

                for (HttpCookie item : cookies) {
                    if (item.getName().equals(cookie.getName())) {
                        cookies.remove(item);
                        break;
                    }
                }
                cookies.add(cookie);
            }
        }


        public List<HttpCookie> get(URI uri) {
            synchronized (lock) {
                List<HttpCookie> result = new ArrayList<>();
                String host = "." + uri.getHost();
                if (".localhost".equals(host))
                    host = ".localhost.local";

                Calendar calendar = Calendar.getInstance();
                for (Map.Entry<String, List<HttpCookie>> cookies : map.entrySet()) {
                    for (HttpCookie cookie : cookies.getValue()) {

                        if (-1 < host.lastIndexOf("." + cookie.getDomain()) &&
                                -1 < uri.getPath().indexOf(cookie.getPath()) &&
                                -1 <= cookie.getMaxAge())

                            result.add(cookie);
                    }
                }

                return result;
            }
        }

        public List<HttpCookie> getCookies() {
            synchronized (lock) {
                Collection<List<HttpCookie>> values = map.values();
                List<HttpCookie> result = new ArrayList<>();
                for (List<HttpCookie> value : values) {
                    result.addAll(value);
                }
                return result;
            }
        }

        public List<URI> getURIs() {
            synchronized (lock) {
                Set<String> keys = map.keySet();
                List<URI> result = new ArrayList<>();
                try {
                    for (String item : keys) {
                        result.add(new URI(item));
                    }
                } catch (Exception e) {
                }
                return result;
            }

        }

        public boolean remove(URI uri, HttpCookie cookie) {
            synchronized (lock) {
                String key = cookie.getDomain() + cookie.getPath();
                List<HttpCookie> cookies = map.get(key);
                if (cookies == null) {
                    return false;
                }
                return cookies.remove(cookie);
            }
        }

        public boolean removeAll() {
            synchronized (lock) {
                map.clear();
                return true;
            }
        }
    }

    public String getCookie(URI uri) {
        try {
            Map<String, List<String>> map = new HashMap<>();
            map = get(uri, map);
            StringBuffer buffer = new StringBuffer();
            for (String item : map.get("Cookie")) {
                buffer.append("; ").append(item);
            }
            if (buffer.length() > 2)
                return buffer.substring(2);
        } catch (IOException e) {
            log.error(e, e);
        }
        return null;
    }

    ;

    public HttpCookie getValue(URI uri, String key) {
        List<HttpCookie> cookie = getCookieStore().get(uri);
        for (HttpCookie item : cookie) {
            if (item.getName().equals(key))
                return item;
        }
        return null;
    }

    public List<HttpCookie> getValue(URI uri) {
        return getCookieStore().get(uri);
    }
}
